#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "raylib.h"
#include "pub_lib.h"
#include "FreeImage.h"
#include "image.h"
#include "dpi_algo.h"

const int screenWidth = 1024;
const int screenHeight = 768;

cpu_image effect_data;
run_state gstate = STATE_IDLE;

char* hint_right = NULL;
char hint_left[128] = "image size";

void show_effect_rchg(void)
{
	show_image(effect_data.rchg, &effect_data,\
		effect_data.rx, effect_data.starty);
}

#define MAX_CMD_NUM 8

//spatial filter
const char command_str_en[MAX_CMD_NUM][MAX_HINT_STRING_LEN] =
{
	//smoothing, ch3.5
	"1. smoothing - linear", //avg
	"2. smoothing - non-linear", //order-statistic
	//sharpening, derivative, ch3.6
	"3. sharpen - laplacian", //second derivative
	"4. sharpen - highboost", //second derivative, Gaussian
	"5. sharpen - gradient", //first-order derivative
	// combining spatial enhancement method, ch3.7
	"6. spatial filter combining",
	//fuzzy, ch3.8
	"7. fuzzy - intensity",
	"8. fuzzy - spatial filter"
};

//空间滤波
const char command_str_cn[MAX_CMD_NUM][MAX_HINT_STRING_LEN] =
{
	"1. 平滑线性滤波器", //均值滤波
	"2. 统计排序滤波器", //中值滤波
	"3. 拉普拉斯算子锐化", //二阶微分，线性
	"4. 高增滤波锐化", //二阶微分，线性，高斯算子
	"5. 梯度锐化", //一阶微分，非线性
	"6. 混合空间滤波", //鸡尾酒
	"7. 模糊集合灰度变换",
	"8. 模糊集合空间滤波"
};

char smooth_linear_image[] = "Fig0334(a)(hubble-original).tif";
char smooth_nonlinear_image[] = "Fig0335(a)(ckt_board_saltpep_prob_pt05).tif";
char sharpen_laplace_image[] = "Fig0338(a)(blurry_moon).tif";
char sharpen_highboost_image[] = "Fig0340(a)(dipxe_text).tif";
char sharpen_grad_image[] = "Fig0342(a)(contact_lens_original).tif";
char combine_image[] = "Fig0343(a)(skeleton_orig).tif";
char fuzzy_intensity_image[] = "Fig0354(a)(einstein_orig).tif";
char fuzzy_filter_image[] = "Fig0359(a)(headCT_Vandy).tif";

static void draw_bottom_his(unsigned int* histo);
static void draw_right_his(unsigned int* histo);

void init_effect_data(void)
{
	memset(&effect_data, 0, sizeof(cpu_image));
	effect_data.win_width = screenWidth;
	effect_data.win_height = screenHeight;
	effect_data.fb_pixels = effect_data.win_width * effect_data.win_height;
	effect_data.startx = 10;
	effect_data.starty = 30;
	effect_data.left_max = get_max_pixel(screenWidth, 0.3f);
	effect_data.right_max = get_max_pixel(screenWidth, 0.7f);
	effect_data.split_line = effect_data.startx + effect_data.left_max + 5;
	effect_data.lx = effect_data.startx;
	effect_data.rx = effect_data.split_line + 5;
}

void init_fb(void)
{
	effect_data.frame_buf = calloc(effect_data.fb_pixels, sizeof(Color));
}

/*
 * 打开图片，读入图片数据
 * 原始图片信息:  ori
 * 左边显示的小图片: left
 * 右边显示的大图片: right
 * 右边显示的大图片的更改后的数据: rchg
 * 右边显示的大图片，中间计算的结果：rchg_data
 * */
int init_effect(char* path)
{
	const char* ext = GetFileExtension(path);

	FIBITMAP* ori;
	ori = open_img(path);
	if (!ori)
		return -1;
	if (!strcmp(ext, ".tif")) {
		read_img(ori, &effect_data);
		if (effect_data.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) {
			printf("only support [0, 255] 1 byte TIF image\n");
			close_img(ori);
			return -1;
		}
	}
	effect_data.show_histo = 0;
	effect_data.ori = (void*)ori;
	effect_data.left = (void*)ori;
	effect_data.right = (void*)ori;
	//check if need scale of left image
	int max = effect_data.left_max; //最长或最宽不超过360
	int neww = max, newh = max;
		if ( effect_data.image_width > max || effect_data.image_height > max) {
		cal_scaled_val(max, effect_data.image_width, effect_data.image_height,\
				&neww, &newh);
		effect_data.left = FreeImage_Rescale(ori, neww, newh, FILTER_BILINEAR);
	}

	//update left_hint[]
	if(effect_data.ori != effect_data.left) {
		sprintf(hint_left, "ori:%dx%d  chg:%dx%d",\
			effect_data.image_width, effect_data.image_height,\
			neww, newh);
	} else {
		sprintf(hint_left, "ori:%d x %d",\
			effect_data.image_width, effect_data.image_height);
	}

	//check if need scale of right image
	max = effect_data.right_max;
	neww = effect_data.image_width;
	newh = effect_data.image_height;
	if (effect_data.image_width > max || effect_data.image_height > max) {
		cal_scaled_val(max, effect_data.image_width, effect_data.image_height, \
			&neww, &newh);
		effect_data.right = FreeImage_Rescale(ori, neww, newh, FILTER_BILINEAR);
	}

	effect_data.rchg = FreeImage_Copy(effect_data.right, 0, 0, neww, newh);
	effect_data.pri  = FreeImage_Copy(effect_data.right, 0, 0, neww, newh);
	//int data[w][h]
	effect_data.rchg_data = calloc(neww * newh, sizeof(float));

	effect_data.image_size = GetPixelDataSize(effect_data.image_width,\
		effect_data.image_height, effect_data.format);
	effect_data.pix_len = get_pixel_bytes(effect_data.image_width,\
		effect_data.image_height, effect_data.image_size);

	return 0;
}

void unload_image(void)
{
	if(effect_data.left != effect_data.ori)
		close_img(effect_data.left);
	if(effect_data.right != effect_data.ori)
		close_img(effect_data.right);
	if(effect_data.ori)
		close_img(effect_data.ori);
	if(effect_data.rchg)
		close_img(effect_data.rchg);
	if(effect_data.pri)
		close_img(effect_data.pri);
	if(effect_data.rchg_data)
		free(effect_data.rchg_data);

}
void destroy_effect(void)
{
	free(effect_data.frame_buf);
	unload_image();
}

void switch_next_image(char* filename)
{
	int ret;
	char path[256];

	clear_fb(&effect_data, RAYWHITE);
	unload_image();
	get_full_path(IMAGE_PATH, filename, path);
	ret = init_effect(path);
	if (ret)
		return;
	show_image(effect_data.left, &effect_data,\
		effect_data.startx, effect_data.starty);
}

void handle_key_down(void)
{
	
}

void handle_key_up(void)
{
}

void handle_lapaction_filter(void)
{
	if(effect_data.order == 0) {
		hint_right = "scaled, press key: space";
		image_filter_lapaction(effect_data.right, effect_data.rchg, 1,\
			effect_data.rchg_data);
		image_filter_scale_show(effect_data.rchg, effect_data.rchg_data);
		effect_data.order = 1;
		show_effect_rchg();
	} else if(effect_data.order == 1) {
		effect_data.order = 2;
		hint_right = "ori + scaled";
		image_filter_lapaction_final(effect_data.right, effect_data.rchg, 2, NULL);
		gstate = STATE_FINISH;
		show_effect_rchg();
	}
}

void handle_gaussian_filter(void)
{
	FIBITMAP* mask;
	mask = effect_data.pri;

	if (effect_data.order == 0) {
		hint_right = "mask = ori - blurred, press space key";
		effect_data.order = 1;
		//3. subtract the blurred image from the original
		//mask = ori - filter, rchg = blurred image
		image_add(effect_data.right, effect_data.rchg, mask, -1);
		printf("模板 = 原图 - 模糊图，为了显示轮廓，再标定一下\n");
		image_filter_scale(mask);
		show_image(mask, &effect_data, effect_data.rx, effect_data.starty);
	}
	else if (effect_data.order == 1) {
		effect_data.order = 2;
		hint_right = "ori + mask, press space key";
		//mask = ori - filter, calc mask
		image_add(effect_data.right, effect_data.rchg, mask, -1);
		//right = ori + mask
		printf("展示： 原图 + 模板\n");
		image_add(effect_data.right, mask, effect_data.rchg, 1);
		show_effect_rchg();
	}
	else if (effect_data.order == 2) {
		effect_data.order = 3;
		hint_right = "ori + 4 * mask, finished";
		gstate = STATE_FINISH;
		//here, rchg = ori + mask
		//so, rchg = rchg + 3 * mask = ori + 4 * mask
		printf("展示： 原图 + 4* 模板\n");
		image_add(effect_data.rchg, mask, effect_data.rchg, 3);
		show_effect_rchg();
	}
}

void handle_mix_filter(void)
{
	FIBITMAP* lapace;
	lapace = effect_data.pri;

	switch (effect_data.order) {
	case 0:
		hint_right = "Laplacian, press key: space";
		printf("拉普拉斯变换，再标定一下\n");
		image_filter_lapaction(effect_data.right, effect_data.rchg, 4,\
			effect_data.rchg_data);
		image_filter_scale_show(effect_data.rchg, effect_data.rchg_data);
		//saved for further use
		image_filter_lapaction(effect_data.right, lapace, 4, NULL);
		break;
	case 1:
		printf("拉普拉斯变换后, 再和原图相加\n");
		hint_right = "ori + lapaction, press key: space";
		image_filter_lapaction_final(effect_data.right, effect_data.rchg, 4, NULL);
		break;
	case 2:
		printf("梯度处理\n");
		hint_right = "sobel grad, press key: space";
		image_filter_grad_sobel(effect_data.right, effect_data.rchg);
		break;
	case 3:
		printf("均值滤波\n");
		hint_right = "smooth: avg, press key: space";
		{
			FIBITMAP* temp; //point to grad filtered image
			temp = copy_img(effect_data.rchg);
			image_filter_avg(temp, effect_data.rchg, 5);
			close_img(temp);
		}
		break;
	case 4:
		printf("上一幅图*拉普拉斯\n");
		hint_right = "grad to avg * lapace, press key: space";
		image_multi(lapace, effect_data.rchg, effect_data.rchg);
		break;
	case 5:
		printf("上一幅图 + 原图\n");
		hint_right = "ori + last, press key: space";
		image_add(effect_data.right, effect_data.rchg, effect_data.rchg, 1);
		break;
	case 6:
		printf("伽马变换\n");
		hint_right = "gamma, finished";
		img_gamma(effect_data.rchg, effect_data.rchg,\
			effect_data.rchg_data, 0.5f);
		gstate = STATE_FINISH;
		break;
	default:
		break;
	}

	show_effect_rchg();
	if(gstate != STATE_FINISH)
		effect_data.order++;
}

/* 模糊几何灰度变换 */
void handle_fuzzy_filter(void)
{
	hint_right = "fuzzy intensity transform";
	gstate = STATE_FINISH;
	printf("模糊集合灰度变换\n");

	fuzzy_intensity_hence(effect_data.right, effect_data.rchg);
	get_histogram(effect_data.rchg, effect_data.chg_histo);
	show_effect_rchg();
}

void handle_key_space(void)
{
	BYTE min, max;

	if(gstate != STATE_WAIT_NEXT)
		return;

	switch(effect_data.cmd) {
		case 1:
			//均值滤波之后，再阈值处理
			//最高亮度的 75% 作为阈值，低于它全部变为0，高于它变为255
			image_get_intensity_range(effect_data.rchg, &min, &max);
			max = max * 0.75f;
			image_binary(effect_data.right, effect_data.rchg, max);
			gstate = STATE_FINISH;
			hint_right = "binary handler";
			show_effect_rchg();
			break;
		case 2:
			gstate = STATE_FINISH;
			hint_right = "filter median";
			image_filter_median(effect_data.right, effect_data.rchg, 3);
			show_effect_rchg();
			break;
		case 3:
			//拉普拉斯算子锐化
			handle_lapaction_filter();
			break;
		case 4:
			handle_gaussian_filter();
			break;
		case 6:
			handle_mix_filter();
			break;
		case 7:
			handle_fuzzy_filter();
			break;
		default:
			break;
	}
}

void draw_bottom_his(unsigned int* histo)
{
	int x, y, xend, yend, w, h;
	w = FreeImage_GetWidth(effect_data.rchg);
	h = FreeImage_GetHeight(effect_data.rchg);
	x = effect_data.rx;
	y = effect_data.starty + h + 5;
	xend = x + w;
	yend = effect_data.win_height - 5;
	DrawText("Origial", x + 20, y + 20, 20, BLACK);
	draw_histogram(effect_data.frame_buf, effect_data.win_width,\
		x, y, xend, yend, histo);
}

void draw_right_his(unsigned int* histo)
{
	int x, y, xend, yend, w, h;
	w = FreeImage_GetWidth(effect_data.rchg);
	h = FreeImage_GetHeight(effect_data.rchg);
	x = effect_data.rx + w + 5;
	y = effect_data.starty;
	xend = effect_data.win_width - 5;
	yend = y + h;
	DrawText("changed", x + 20, y + 20, 20, BLACK);
	draw_histogram(effect_data.frame_buf, effect_data.win_width,\
		x, y, xend, yend, histo);
}

//模糊集合灰度变换
void handle_fuzzy_transform(void)
{
	int max_width = effect_data.right_max;
	int max_new;
	max_new = split_right_window(effect_data.win_width, 0.7f);
	effect_data.right_max = max_new;
	switch_next_image(fuzzy_intensity_image);

	effect_data.right_max = max_width;
	image_histogram_equa(effect_data.right, effect_data.rchg,\
		effect_data.sk_data);
	show_effect_rchg();

	get_histogram(effect_data.right, effect_data.ori_histo);
	get_histogram(effect_data.rchg, effect_data.chg_histo);
	effect_data.show_histo = 7;
	gstate = STATE_WAIT_NEXT;
	hint_right = "histogram equalization, press key: space";
	printf("先显示直方图均衡过的图片\n");
}

void special_effect(int cmd)
{
	//repeate press same key
	if (effect_data.cmd == cmd)
		return;

	if(cmd <= MAX_CMD_NUM) {
		effect_data.cmd = cmd;
		hint_right = (char*)command_str_en[cmd - 1];
		SetWindowTitle(command_str_cn[cmd - 1]);
		printf("%s\n", command_str_cn[cmd - 1]);
	}

	if(cmd <= 8 )
		effect_data.order = 0;

	int show = 0;

	switch (cmd) {
	case 1: //均值滤波
		switch_next_image(smooth_linear_image);
		printf("正在均值滤波 ...\n");
		//15 * 15 filter
		image_filter_avg(effect_data.right, effect_data.rchg, 15);
		show = 1;
		hint_right = "OK with AVG, press key: space";
		printf("完成均值滤波，按空格键继续\n");
		gstate = STATE_WAIT_NEXT;
		break;
	case 2: //中值滤波
		switch_next_image(smooth_nonlinear_image);
		hint_right = "avg first, press key: space";
		gstate = STATE_WAIT_NEXT;
		image_filter_avg(effect_data.right, effect_data.rchg, 3);
		show = 1;
		break;
	case 3: //拉普拉斯算子锐化
		switch_next_image(sharpen_laplace_image);
		image_filter_lapaction(effect_data.right, effect_data.rchg, 1,\
			effect_data.rchg_data);
		img_gamma(effect_data.rchg, effect_data.rchg, effect_data.rchg_data, 0.9f);
		hint_right = "lapaction convert, press key: space";
		gstate = STATE_WAIT_NEXT;
		show = 1;
		break;
	case 4: //高增滤波锐化, Gaussian filter
		switch_next_image(sharpen_highboost_image);
		hint_right = "blurred: Gaussian, press space key";
		//5 * 5 filter, sigma = 3
		image_filter_gaussian(effect_data.right, effect_data.rchg, 5, 3.0f);
		gstate = STATE_WAIT_NEXT;
		show = 1;
		break;
	case 5: //梯度锐化
		hint_right = "Grad: Sobel operators";
		switch_next_image(sharpen_grad_image);
		image_filter_grad_sobel(effect_data.right, effect_data.rchg);
		show = 1;
		break;
	case 6: //混合空间滤波
		switch_next_image(combine_image);
		hint_right = "original, press key: space";
		gstate = STATE_WAIT_NEXT;
		show = 1;
		break;
	case 7: //模糊集合灰度变换
		switch_next_image(fuzzy_intensity_image);
		handle_fuzzy_transform();
		break;
	case 8: //模糊集合空间滤波
		switch_next_image(fuzzy_filter_image);
		fuzzy_spatial_filter_hence(effect_data.right, effect_data.rchg);
		image_filter_scale(effect_data.rchg);
		show = 1;
		break;

	case KEY_DOWN:
		handle_key_down();
		break;
	case KEY_UP:
		handle_key_up();
		break;
	case KEY_SPACE:
		handle_key_space();
		break;
	default:
		SetWindowTitle("空间滤波");
		hint_right = "not support command";
		break;
	}//end switch
	 //
	if(show)
		show_effect_rchg();
}


int main(int argc, char** argv)
{
	// Initialization
	//--------------------------------------------------------------------------------------
	FreeImage_Initialise(TRUE);
	init_effect_data();
	init_fb();

	InitWindow(screenWidth, screenHeight, "空间滤波");
	// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
	SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
	//--------------------------------------------------------------------------------------

	int key = 0;
	int change = 0;
	Vector2 mouse = GetMousePosition();
	hint_right = "press number key";
	print_info_cn(command_str_cn, MAX_CMD_NUM);

	// Main game loop
	while (!WindowShouldClose())    // Detect window close button or ESC key
	{
		// Update
		key = GetKeyPressed();
		if (key >= '1' && key <= '9') {
			change = 1;
			key = key - '0';
		} else if(key == KEY_DOWN || key == KEY_UP){
			change = 1;
		}
		else if(key == KEY_SPACE) {
			change = 1;
		}
		else if (key == ESC_KEY) {
			break;
		}

		if (change) {
			change = 0;
			special_effect(key);
		}
		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------

		// Draw
		//----------------------------------------------------------------------------------
		BeginDrawing();
		ClearBackground(RAYWHITE);

		DrawText(hint_left, effect_data.startx, 5, 20, BLACK);
		if (hint_right) {
			DrawText(hint_right, effect_data.rx, 0, 20, BLACK);
		}
		show_info_en(command_str_en, MAX_CMD_NUM, effect_data.startx,\
			effect_data.left_max + effect_data.starty + 20);
		draw_image(effect_data.frame_buf, effect_data.win_width,\
			effect_data.win_height);
		DrawLine(effect_data.split_line, effect_data.starty,\
			effect_data.split_line, GetScreenHeight(), GRAY);

		if (effect_data.show_histo == 7) { //模糊集合灰度变换
			draw_right_his(effect_data.chg_histo);
			draw_bottom_his(effect_data.ori_histo);
		}

		mouse = GetMousePosition();
		DrawText(TextFormat("mouse(%d, %d)", (int)mouse.x, (int)mouse.y), \
			GetScreenWidth() - 200, 10, 20, BLACK);

		EndDrawing();
		//----------------------------------------------------------------------------------
	}

	// De-Initialization
	destroy_effect();
	FreeImage_DeInitialise();

	//--------------------------------------------------------------------------------------
	CloseWindow();        // Close window and OpenGL context
	//--------------------------------------------------------------------------------------

	return 0;
}


